#ifndef SIMODO_LSP_ServerContext_h
#define SIMODO_LSP_ServerContext_h

#include "simodo/lsp-server/ReportCombiner.h"

#include "simodo/lsp-client/ClientParams.h"
#include "simodo/lsp-client/Location.h"
#include "simodo/lsp-client/CompletionParams.h"
#include "simodo/lsp-client/ServerCapabilities.h"
#include "simodo/lsp-client/SimodoCapabilities.h"
#include "simodo/variable/json/Rpc.h"
#include "simodo/tp/ThreadPool.h"
#include "simodo/inout/log/Logger_interface.h"

#include <map>
#include <memory>

namespace simodo::lsp
{

    class DocumentContext;

    class DocumentOperation_interface
    {
    public:
        virtual ~DocumentOperation_interface() = default;

        virtual bool analyze(const std::u16string & text, inout::Reporter_abstract & m) = 0;
        
        virtual bool checkDependency(const std::string &uri) const = 0;

        virtual variable::Value produceHoverResponse(const lsp::Position &position) const = 0;
        virtual variable::Value produceGotoDeclarationResponse(const lsp::Position &position) const = 0;
        virtual variable::Value produceGotoDefinitionResponse(const lsp::Position &pos) const = 0;
        virtual variable::Value produceCompletionResponse(const lsp::CompletionParams &completionParams) const = 0;
        virtual variable::Value produceSemanticTokensResponse() const = 0;
        virtual variable::Value produceDocumentSymbolsResponse() const = 0;

        virtual variable::Value produceSimodoCommandResponse(const std::u16string & command_name, std::u16string text) const = 0;
    };

    class DocumentOperationFactory_interface
    {
    public:
        virtual ~DocumentOperationFactory_interface() = default;

        virtual std::unique_ptr<DocumentOperation_interface> create(DocumentContext &doc, const std::string & languageId) = 0;
    };

    enum class ServerState
    {
        None,
        Work,
        Shutdown
    };

    class ServerContext
    {
        std::u16string                  _server_name;
        simodo::lsp::ServerCapabilities _server_capabilities;
        simodo::lsp::SimodoCapabilities _simodo_capabilities;
        DocumentOperationFactory_interface &    
                                        _document_operation_factory;
        inout::Logger_interface &       _log;
        std::istream &                  _is;
        std::ostream &                  _os;

        tp::ThreadsafeQueue<simodo::variable::JsonRpc>
                                        _queue_for_sending;
        tp::ThreadPool                  _tp;
        std::thread                     _response_thread;
        bool                            _done = false;
        ReportCombiner                  _m;
        std::map<std::string, std::unique_ptr<DocumentContext>>
                                        _documents;
        mutable std::mutex              _documents_mutex;
        ServerState                     _state = ServerState::None;
        simodo::lsp::ClientParams       _client_params;
        bool                            _save_mode = false;

    public:
        ServerContext() = delete;
        ServerContext(std::u16string server_name,
                    simodo::lsp::ServerCapabilities server_capabilities,
                    simodo::lsp::SimodoCapabilities simodo_capabilities,
                    DocumentOperationFactory_interface & document_operation_factory,
                    simodo::inout::Logger_interface & log, 
                    std::istream & is, 
                    std::ostream & os, 
                    bool save_mode = false);
        ~ServerContext();

        const simodo::lsp::ServerCapabilities & server_capabilities() const { return _server_capabilities; }
        const simodo::lsp::SimodoCapabilities & simodo_capabilities() const { return _simodo_capabilities; }
        DocumentOperationFactory_interface &    document_operation_factory() { return _document_operation_factory; }

    public:
        simodo::tp::ThreadsafeQueue<simodo::variable::JsonRpc> &
                                    sending() { return _queue_for_sending; }
        simodo::tp::ThreadPool &    tp() { return _tp; }
        ServerState                 state() const { return _state; }
        bool                        save_mode() const { return _save_mode; }

        bool                        openDocument(const simodo::variable::Object &doc_params);
        bool                        changeDocument(const simodo::variable::Object &doc_params);
        bool                        closeDocument(const std::string &uri);
        DocumentContext *           findDocument(const std::string &uri);
        const DocumentContext *     findDocument(const std::string &uri) const;

        bool                        findAndCopy(const std::string &uri, std::u16string &content);

    public:
        simodo::inout::Logger_interface & 
                                    log() const { return _log; }
        bool                        is_done() const { return _done; }
        bool                        run();
        simodo::variable::JsonRpc   initialize(simodo::lsp::ClientParams params, int id);
        void                        initialized();
        bool                        shutdown();
        void                        exit();

    private:
        void                        sendResponse_loop();
    };

}

#endif // SIMODO_LSP_ServerContext_h